<?php
/**
 * ErrorManager
 *
 * Framework ErrorManager
 *
 * @category	General
 * @package		dayscript
 * @subpackage
 * @author		Nelson Daza <ndaza@dayscript.com>
 * @copyright	2009 Dayscript Ltda.
 * @license
 * @version		1.0
 * @version		$Revision: 0 $
 * @filesource
 * @link		http://www.dayscript.com
 * @link		{docsLink}
 * @uses
 * @since		1.0
 * @modifiedby	$LastChangedBy: Nelson Daza $
 * @modified	$Date: 2009-08-26 $
 */
 
namespace dayscript;

/**#@+
 * Verification of definition for internal use
 */
if( !\defined( 'E_UNKNOWN' ) )
	\define( 'E_UNKNOWN', 0 );
if( !\defined( 'E_STRICT' ) )
	\define( 'E_STRICT', E_USER_NOTICE << 1 );
if( !\defined( 'E_RECOVERABLE_ERROR' ) )
	\define( 'E_RECOVERABLE_ERROR', E_STRICT << 1 );
if( !\defined( 'E_DEPRECATED' ) )
	\define( 'E_DEPRECATED', E_RECOVERABLE_ERROR << 1 );
if( !\defined( 'E_USER_DEPRECATED' ) )
	\define( 'E_USER_DEPRECATED', E_DEPRECATED << 1 );
/**#@-*/
/**
 * Singleton
 *
 * @category	General
 * @package		dayscript
 * @subpackage
 * @author		Nelson Daza <ndaza@dayscript.com>
 * @copyright	2009 Dayscript Ltda.
 * @license
 * @version		1.0
 * @link		http://www.dayscript.com
 * @link		{docsLink}
 * @uses		dayscript\log\LogManager, \ErrorException
 * @since		1.0
 * @modifiedby	$LastChangedBy: Nelson Daza $
 * @modified	$Date: 2009-08-26 $
 */
class ErrorManager {
	/**
	 * Uproar mode
	 *
	 * This wil turn every error into an Exception;
	 * @var boolean $uproarMode
	 */
	private static $uproarMode = false;
	/**
	 * Last exception
	 * @var \exception\Exception $exception las exception catched
	 */
	private static $exception = null;
	/**
	 * Error names
	 * @see http://www.php.net/manual/en/errorfunc.constants.php
	 * @var array $levels
	 */
	public static $levels = array(
		E_UNKNOWN			=>	'UNKNOWN ERROR',
		E_ERROR				=>	'ERROR',
		E_WARNING			=>	'WARNING',
		E_PARSE				=>	'PARSING ERROR',
		E_NOTICE			=>	'NOTICE',
		E_CORE_ERROR		=>	'CORE ERROR',
		E_CORE_WARNING		=>	'CORE WARNING',
		E_COMPILE_ERROR		=>	'COMPILE ERROR',
		E_COMPILE_WARNING	=>	'COMPILE WARNING',
		E_USER_ERROR		=>	'USER ERROR',
		E_USER_WARNING		=>	'USER WARNING',
		E_USER_NOTICE		=>	'USER NOTICE',
		E_STRICT			=>	'STRICT NOTICE',
		E_RECOVERABLE_ERROR	=>	'RECOVERABLE ERROR',
		E_DEPRECATED		=>	'DEPRECATED',
		E_USER_DEPRECATED	=>	'USER DEPRECATED',
		E_ALL				=>	'MAX UNKNOWN ERROR'
	);
	/**
	 * ErrorManager Contructor
	 * @param string $message
	 * @param int $code
	 */
	private function __construct( )	{
		
	}
	/**
	 * Whether the uproadMode is ON
	 * @return boolean $uproarMode
	 */
	public static function isUproarMode( )	{
		return self::$uproarMode;
	}
	/**
	 * Starts/stops uproar mode
	 * In this mode all triggered user errors will turn into Exceptions wich is the recomended state.
	 */
	public static function setUproarMode( $uproar )	{
		self::$uproarMode = $uproar;
	}
	/**
	 * Global exception handler
	 * @param \Exception $exception
	 * @return boolean true Avoid php default handler
	 */
	public static function exceptionHandler( \Exception $exception )	{
		$code = ( isset( self::$levels[$exception->getCode( )] ) ? $exception->getCode( ) : \E_UNKNOWN );
		$backtrace = ( \count( $exception->getTrace( ) ) == 0 ? self::getBacktraceAsString( 1 ) : "\n" . $exception->getTraceAsString( ) );
		$str = self::$levels[$code];
		$text = \sprintf ("%s: Uncaught exception '%s' with message '%s' in %s:%d%s", $str, \get_class( $exception ), $exception->getMessage( ), $exception->getFile( ), $exception->getLine( ), ( $backtrace ? "\nStack trace:" . $backtrace : '' ) );
		
		$threshold = $exception->getCode( );
		$system = ( $code != \E_UNKNOWN && \strpos( $str, 'USER' ) === false );
		
		if( !$system )	{
			if( ( \strpos( $str, 'WARNING' ) !== false ) )
				$threshold = \dayscript\log\LogManager::LOG_WARNING;
			else if( ( \strpos( $str, 'NOTICE' ) !== false ) )
				$threshold = \dayscript\log\LogManager::LOG_NOTICE;
			else if( ( \strpos( $str, 'INFO' ) !== false ) )
				$threshold = \dayscript\log\LogManager::LOG_INFO;
			else
				$threshold = \dayscript\log\LogManager::LOG_ERROR;
		}
		
		\dayscript\log\LogManager::log( $text, $threshold, $system );
		
		self::$exception = $exception;
		return true;
	}
	/**
	 * Global error handler
	 * @param int $errno
	 * @param string $errstr
	 * @param string $errfile
	 * @param int $errline
	 * @param mixed $errcontext
	 * @return boolean Avoid php default handler
	 */
	public static function errorHandler( $errno, $errstr, $errfile = null, $errline = 0, $errcontext = null )	{
		$errno = ( isset( self::$levels[$errno] ) ? $errno : \E_UNKNOWN );
		if( $errcontext != null )
			$errcontext = null;
		$backtrace = self::getBacktraceAsString( 1 );
		$str = self::$levels[$errno];
		$text = \sprintf ("%s: '%s' in %s:%d%s", $str, $errstr, $errfile, $errline, ( $backtrace ? "\nStack trace:" . $backtrace : '' ) );
		
		$threshold = $errno;
		$system = ( $errno != \E_UNKNOWN && \strpos( $str, 'USER' ) === false );
		if( !$system )	{
			if( ( \strpos( $str, 'WARNING' ) === false ) )
				$threshold = \dayscript\log\LogManager::LOG_WARNING;
			else if( ( \strpos( $str, 'NOTICE' ) === false ) )
				$threshold = \dayscript\log\LogManager::LOG_NOTICE;
			else if( ( \strpos( $str, 'INFO' ) === false ) )
				$threshold = \dayscript\log\LogManager::LOG_INFO;
			else
				$threshold = \dayscript\log\LogManager::LOG_ERROR;
		}
			
		
		if( !self::$uproarMode )
			\dayscript\log\LogManager::log( $text, $threshold, $system, \error_reporting( ) );
		
		$exception = new \ErrorException( $errstr, $errno, 0, $errfile, $errline );
		
		self::$exception = $exception;
		
		if( self::$uproarMode )
			throw $exception;
		/*
		if( \strpos( $str, 'ERROR' ) !== false && \strpos( $str, 'RECOVERABLE' ) === false )	{
			\dayscript\log\LogManager::flush( );
			exit( ); // Buggie fix for sub catchers
		}
		*/
			
		return true;
	}
	/**
	 * Global script shutdown Handler
	 * It will report if some error was found after shoutdown.
	 * In some cases the error does not necesarili kill the script but the
	 * @param mixed $params
	 */
	public static function shutdownHandler( $params = null )	{
		\dayscript\log\LogManager::log( 'SHUTDOWN ErrorManager (1).', \dayscript\log\LogManager::LOG_USER_INFO );
		\flush( );
		self::setUproarMode( false );
		$lastError = \error_get_last( );
		if( \is_array( $lastError ) )	{
			self::errorHandler( $lastError['type'], 'FATAL TERMINAL ERROR: ' . $lastError['message'], $lastError['file'], $lastError['line'], $params );
			while ( \ob_get_level( ) > 0 )
				\ob_end_flush( );
		}
		\flush( );
	}
	/**
	 * General handlers registration
	 */
	public static function createGlobalHandlers( )	{
		\set_exception_handler( array( __CLASS__, 'exceptionHandler' ) );
		\set_error_handler( array( __CLASS__, 'errorHandler' ) );
	}
	/**
	 * Registers its shutdown function
	 */
	public static function registerShutdown( )	{
		\register_shutdown_function( array( __CLASS__, 'shutdownHandler' ) );
	}
	/**
	 * Returns a backtrace
	 * @param int $jumpTrace
	 * @param int $limit
	 * @return array $backtrace
	 */
	public static function getBacktrace( $jumpTrace = 0, $limit = 0 )	{
		$backtrace = \debug_backtrace( );
		for ( $c = 0; $c <= $jumpTrace; $c ++ )
			\array_shift( $backtrace );
		if( $limit > 0 )	{
			while ( \count( $backtrace ) > $limit )
				\array_pop( $backtrace );
		}
		return $backtrace;
	}
	/**
	 * Returns a backtrace as string
	 * @param int $jumpTrace
	 * @param int $limit
	 * @return string $backtrace
	 */
	public static function getBacktraceAsString( $jumpTrace = 0, $limit = 0 )	{
		$array = self::getBacktrace( ++$jumpTrace, $limit );
		$backtrace = '';
		$cont = 0;
		foreach( $array as $stack )
			$backtrace .= \sprintf ("\n#%d %s in %s:%d", $cont++, ( isset( $stack['class'] ) ? $stack['class'] . ':' : '' ) . $stack['function'] . '( )', ( isset( $stack['file'] ) ? $stack['file'] : '' ), ( isset( $stack['line'] ) ? $stack['line'] : '0' ) );
		return $backtrace;
	}
}
